﻿using System;
using UFIDA.U9.WSS.ProxyService.Exceptions;
using UFIDA.U9.WSS.ProxyService.Json;
using UFIDA.U9.WSS.ProxyService.Models;
using UFIDA.U9.WSS.ProxyService.Utils;
using UFSoft.UBF.Util.Log;

namespace UFIDA.U9.WSS.ProxyService.Token
{
    /// <summary>
    ///     Token帮助类
    /// </summary>
    public class TokenHelper
    {
        private static TokenHelper _instance;
        private static readonly object Lock = new object();
        private static readonly object InitLock = new object();
        private static readonly ILogger Logger = LoggerManager.GetLogger(typeof(TokenHelper));

        //Token是否需要用户密码
        private bool _getTokenNeedUserPassword;
        private bool _isInited;

        //Token有效时长(分钟)
        private int _tokenEffectiveMinutes = -1;

        //Token 密钥
        private string _tokenSecretKey = string.Empty;

        private TokenHelper()
        {
        }

        public static TokenHelper Instance
        {
            get
            {
                if (_instance != null)
                {
                    if (!_instance._isInited)
                    {
                        _instance.Initinal();
                    }

                    return _instance;
                }

                lock (Lock)
                {
                    if (_instance == null)
                    {
                        _instance = new TokenHelper();
                        _instance.Initinal();
                    }
                    else if (!_instance._isInited)
                    {
                        _instance.Initinal();
                    }

                    return _instance;
                }
            }
        }

        /// <summary>
        ///     初始化
        /// </summary>
        private void Initinal()
        {
            lock (InitLock)
            {
                if (_isInited) return;
                Logger.Debug("TokenHelper init...");
                //Token 密钥
                _tokenSecretKey = ConfigurationHelper.GetAppSettingValue(Constant.TokenSecretKeyName);
                //Token有效时长
                _tokenEffectiveMinutes = ConfigurationHelper.GetTokenEffectiveMinutes();
                //Token是否需要用户认证
                _getTokenNeedUserPassword = ConfigurationHelper.GetGetTokenNeedUserPassword();
                _isInited = true;
            }
        }

        /// <summary>
        ///     创建一个token
        /// </summary>
        /// <param name="contextInfo"></param>
        /// <returns></returns>
        public TokenReturnData CreateToken(ContextInfo contextInfo)
        {
            if (contextInfo == null)
                throw new WSException(ExceptionCodes.TokenExceptionBaseCode, "contextInfo is null");
            //初始化
            if (!contextInfo.IsInit) contextInfo.ValidateAndInit(_getTokenNeedUserPassword);
            //失效时间
            DateTime invalidTime = _tokenEffectiveMinutes > 0
                ? DateTime.Now.AddMinutes(_tokenEffectiveMinutes).AddSeconds(1)
                : DateTime.MaxValue;
            TokenData tokenData = new TokenData(contextInfo, invalidTime);
            //token串
            string tokenUnEncryptString = JsonHelper.ToJson(tokenData);
            try
            {
                string token = DataProtector.Encrypt(_tokenSecretKey, tokenUnEncryptString);
                TokenReturnData tokenReturnData = new TokenReturnData();
                tokenReturnData.AccessToken = token;
                if (invalidTime == DateTime.MaxValue)
                {
                    tokenReturnData.ExpiresIn = -1;
                }
                else
                {
                    TimeSpan ts = invalidTime - DateTime.Now;
                    tokenReturnData.ExpiresIn = (int) ts.TotalSeconds;
                    tokenReturnData.ExpiresTime = invalidTime;
                }

                return tokenReturnData;
            }
            catch (Exception ex)
            {
                Logger.Error("encrypt token exception :{0}", tokenUnEncryptString);
                Logger.Error(ex);
                throw new WSException(ExceptionCodes.TokenCreateFail, "get token fail", ex);
            }
        }

        /// <summary>
        ///     校验Token
        /// </summary>
        /// <param name="tokenString"></param>
        /// <param name="contextInfo"></param>
        /// <returns></returns>
        public bool ValidateToken(string tokenString, out ContextInfo contextInfo)
        {
            if (string.IsNullOrEmpty(tokenString))
                throw new WSException(ExceptionCodes.TokenIsInEmpty, "token is empty");
            TokenData tokenData;
            try
            {
                string tokenUnEncryptString = DataProtector.Decrypt(_tokenSecretKey, tokenString);
                tokenData = JsonHelper.FromJson<TokenData>(tokenUnEncryptString);
            }
            catch (Exception ex)
            {
                Logger.Error("decrpt token exception :{0}", tokenString);
                Logger.Error(ex);
                throw new WSException(ExceptionCodes.TokenIsIncorrect, "token is incorrect");
            }

            if (tokenData == null)
            {
                Logger.Error("token is incorrect :{0}", tokenString);
                throw new WSException(ExceptionCodes.TokenIsIncorrect, "token is incorrect");
            }

            //已失效
            if (DateTime.Now > tokenData.InvalidTime)
            {
                Logger.Error("token is invalid :{0}", tokenString);
                throw new WSException(ExceptionCodes.TokenIsInvalid, "token is invalid");
            }

            contextInfo = tokenData.TransContextInfo();
            //Token转化过来的Context,视为已经初始化过的
            contextInfo.IsInit = true;
            return true;
        }

        /// <summary>
        ///     刷新Token
        /// </summary>
        /// <param name="tokenString"></param>
        /// <param name="contextInfo"></param>
        /// <returns></returns>
        public TokenReturnData RefreshToken(string tokenString, ContextInfo contextInfo = null)
        {
            ContextInfo oldContextInfo;
            //校验Token
            ValidateToken(tokenString, out oldContextInfo);
            if (contextInfo == null) return CreateToken(oldContextInfo);
            //重新初始化
            contextInfo.ValidateAndInit(false);
            //重新生成一个Token
            return CreateToken(contextInfo);
        }
    }
}